#ifndef LONGKEY_NET_HTTP_CLIENT_H_
#define LONGKEY_NET_HTTP_CLIENT_H_

#include <Windows.h>
#include <atlstr.h>

#if !defined(_WININET_)
#include <winhttp.h>

// The following definitions are missing in winhttp.h
#define INTERNET_MAX_HOST_NAME_LENGTH	256
#define INTERNET_MAX_USER_NAME_LENGTH	128
#define INTERNET_MAX_PASSWORD_LENGTH	128
#define INTERNET_MAX_PORT_NUMBER_LENGTH	5
#define INTERNET_MAX_PORT_NUMBER_VALUE	65535
#define INTERNET_MAX_PATH_LENGTH		2048
#define INTERNET_MAX_SCHEME_LENGTH		32
#define INTERNET_MAX_URL_LENGTH			(INTERNET_MAX_SCHEME_LENGTH \
										+ sizeof("://") \
										+ INTERNET_MAX_PATH_LENGTH)
#endif

// This is a definition we made up for convenience, just to be consistent with
// the other types of network access.
#define WINHTTP_ACCESS_TYPE_AUTO_DETECT 2

#include "../base/basictypes.h"
#include "../base/object_factory.h"

const TCHAR* const kHttpGetMethod = _T("GET");
const TCHAR* const kHttpPostMethod = _T("POST");
const TCHAR* const kHttpContentLengthHeader = _T("Content-Length");
const TCHAR* const kHttpContentTypeHeader = _T("Content-Type");
const TCHAR* const kHttpLastModifiedHeader = _T("Last-Modified");
const TCHAR* const kHttpIfModifiedSinceHeader = _T("If-Modified-Since");
const TCHAR* const kHttpPostTextContentType = _T("application/x-www-form-urlencoded");
const TCHAR* const kHttpPostRawContentType = _T("application/octet-stream");
const TCHAR* const kHttpBinaryContentType =_T("binary");
const TCHAR* const kHttpXmlContentType =_T("application/xml");


class HttpClient
{
public:
	enum HttpStack { WINHTTP, WININET };

	// Use registry for network configuration info.
	static const int kAccessTypeDefaultProxy  = 0;
	// Unconditional direct connection.
	static const int kAccessTypeNoProxy       = 1;
	// Use the specified proxy server.
	static const int kAccessTypeNamedProxy    = 3;

	// GetFactory and DeleteFactory methods below are not thread safe.
	// The caller must initialize and cleanup the factory before going
	// multithreaded.
	//
	// Gets the singleton instance of the object factory.
	typedef Factory<HttpClient, HttpStack> Factory;
	static Factory& GetFactory();

	// Cleans up the object factory.
	static void DeleteFactory();

	struct ProxyInfo {
		uint32 access_type;
		const TCHAR* proxy;
		const TCHAR* proxy_bypass;
	};

	struct CurrentUserIEProxyConfig {
		bool auto_detect;
		const TCHAR* auto_config_url;
		const TCHAR* proxy;
		const TCHAR* proxy_bypass;
	};

	struct AutoProxyOptions {
		uint32 flags;
		uint32 auto_detect_flags;
		const TCHAR* auto_config_url;
		bool auto_logon_if_challenged;
	};

	virtual ~HttpClient() {}

	// Initializes the use of http functions by loading the corresponding http
	// stack.
	virtual HRESULT Initialize() = 0;

	// Adds one or more http request headers to the http request.
	virtual HRESULT AddRequestHeaders(HINTERNET request,
		const TCHAR* headers,
		int length,
		uint32 modifiers) = 0;

	// Determines whether WinHTTP is available.
	virtual HRESULT CheckPlatform() = 0;

	// Closes an http handle.
	virtual HRESULT Close(HINTERNET handle) = 0;

	// Specifies the initial server and port of an http request.
	virtual HRESULT Connect(HINTERNET session_handle,
		const TCHAR* server,
		int port,
		HINTERNET* connection_handle) = 0;

	// Breaks urls into component parts.
	virtual HRESULT CrackUrl(const TCHAR* url,
		uint32 flags,
		CString* scheme,
		CString* server,
		int* port,
		CString* url_path,
		CString* extra_info) = 0;

	// Builds a url from component parts.
	virtual HRESULT CreateUrl(const TCHAR* scheme,
		const TCHAR* server,
		int port,
		const TCHAR* url_path,
		const TCHAR* extra_info,
		uint32 flags,
		CString* url) = 0;


	// Finds the url for the proxy auto-configuration (PAC) file.
	// It reports the url of the PAC file but it does not download the file.
	virtual HRESULT DetectAutoProxyConfigUrl(uint32 flags,
		CString* auto_config_url) = 0;

	// Gets the proxy configuration from the registry.
	virtual HRESULT GetDefaultProxyConfiguration(ProxyInfo* proxy_info) = 0;

	// Gets the Internet Explorer configuration for the current user.
	virtual HRESULT GetIEProxyConfiguration(
		CurrentUserIEProxyConfig* proxy_info) = 0;

	// Gets the proxy information for an url. This function implements the
	// web proxy auto-discovery (WPAD) protocol for automatically configuring
	// the proxy settings for an http request. The WPAD protocol downloads a
	// proxy auto-configuration (PAC) file, which is a script that identifies
	// the proxy server to use for a given target url.
	virtual HRESULT GetProxyForUrl(HINTERNET session_handle,
		const TCHAR* url,
		const AutoProxyOptions* auto_proxy_options,
		ProxyInfo* proxy_info) = 0;

	// Opens a new http session. The http session contains user specific state
	// such as cookies, proxy, and authentication credentials.
	virtual HRESULT Open(const TCHAR* user_agent,
		uint32 access_type,
		const TCHAR* proxy_name,
		const TCHAR* proxy_bypass,
		DWORD flags,
		HINTERNET* session_handle) = 0;

	// Specifies the http request.
	virtual HRESULT OpenRequest(HINTERNET connection_handle,
		const TCHAR* verb,
		const TCHAR* uri,
		const TCHAR* version,
		const TCHAR* referrer,
		const TCHAR** accept_types,
		uint32 flags,
		HINTERNET* request_handle) = 0;

	// Returns the authorization schemes that are supported by the server or
	// the proxy server.
	virtual HRESULT QueryAuthSchemes(HINTERNET request_handle,
		uint32* supported_schemes,
		uint32* first_scheme,
		uint32* auth_target) = 0;

	// Returns the amount of data, in bytes, available to be read.
	virtual HRESULT QueryDataAvailable(HINTERNET request_handle,
		DWORD* num_bytes) = 0;

	// Retrieves header information associated with the request.
	virtual HRESULT QueryHeaders(HINTERNET request_handle,
		uint32 info_level,
		const TCHAR* name,
		void* buffer,
		DWORD* buffer_length,
		DWORD* index) = 0;

	// Queries an internet option.
	virtual HRESULT QueryOption(HINTERNET handle,
		uint32 option,
		void* buffer,
		DWORD* buffer_length) = 0;

	// Reads response data.
	virtual HRESULT ReadData(HINTERNET request_handle,
		void* buffer,
		DWORD buffer_length,
		DWORD* bytes_read) = 0;

	// Waits to receive the response to an HTTP request. When ReceiveResponse
	// completes successfully, the status code and response headers have been
	// received.
	virtual HRESULT ReceiveResponse(HINTERNET request_handle) = 0;

	// Sends the request to the server. It also allows to send optional data
	// for POST requests.
	virtual HRESULT SendRequest(HINTERNET request_handle,
		const TCHAR* headers,
		DWORD headers_length,
		const void* optional_data,
		DWORD optional_data_length,
		DWORD content_length,
		DWORD_PTR context) = 0;

	// Sets the authentication credentials.
	virtual HRESULT SetCredentials(HINTERNET request_handle,
		uint32 auth_targets,
		uint32 auth_scheme,
		const TCHAR* user_name,
		const TCHAR* password) = 0;

	// Sets the proxy configuration in registry.
	virtual HRESULT SetDefaultProxyConfiguration(const ProxyInfo& proxy_info) = 0;

	// Sets an internet option, either for the session or the request handle.
	virtual HRESULT SetOption(HINTERNET handle,
		uint32 option,
		const void* buffer,
		DWORD buffer_length) = 0;

	typedef void (__stdcall *StatusCallback)(HINTERNET handle,
		uint32 context,
		uint32 status,
		void* status_information,
		size_t status_info_length);
	virtual StatusCallback SetStatusCallback(HINTERNET handle,
		StatusCallback callback,
		uint32 flags) = 0;

	// Sets timeout values for this http request.
	virtual HRESULT SetTimeouts(HINTERNET handle,
		int resolve_timeout_ms,
		int connect_timeout_ms,
		int send_timeout_ms,
		int receive_timeout_ms) = 0;

	// Writes data to the server.
	virtual HRESULT WriteData(HINTERNET request_handle,
		const void* buffer,
		DWORD bytes_to_write,
		DWORD* bytes_written) = 0;

	//
	// Http helpers.
	//
	// Builds one http header entry, with the following format:
	//    name: value\r\n
	static CString BuildRequestHeader(const TCHAR* name, const TCHAR* value);

	enum StatusCodeClass {
		STATUS_CODE_NOCODE        = 0,
		STATUS_CODE_INFORMATIONAL = 100,
		STATUS_CODE_SUCCESSFUL    = 200,
		STATUS_CODE_REDIRECTION   = 300,
		STATUS_CODE_CLIENT_ERROR  = 400,
		STATUS_CODE_SERVER_ERROR  = 500
	};
	// Returns the class of a status code, such as 100, 200...
	static StatusCodeClass GetStatusCodeClass(int status_code);

	HRESULT QueryHeadersString(HINTERNET request_handle,
		uint32 info_level,
		const TCHAR* name,
		CString* value,
		DWORD* index);
	HRESULT QueryHeadersInt(HINTERNET request_handle,
		uint32 info_level,
		const TCHAR* name,
		int* value,
		DWORD* index);

	HRESULT QueryOptionString(HINTERNET handle, uint32 option, CString* value);
	HRESULT QueryOptionInt(HINTERNET handle, uint32 option, int* value);

	HRESULT SetOptionString(HINTERNET handle, uint32 option, const TCHAR* value);
	HRESULT SetOptionInt(HINTERNET handle, uint32 option, int value);

protected:
	HttpClient() {}

private:
	static Factory* factory_;
	DISALLOW_EVIL_CONSTRUCTORS(HttpClient);
};

// Creates an http client, depending on what is available on the platform.
// WinHttp is preferred over WinInet.
HttpClient* CreateHttpClient();

const HttpClient::StatusCallback kInvalidStatusCallback =
reinterpret_cast<HttpClient::StatusCallback>(
	WINHTTP_INVALID_STATUS_CALLBACK);

#endif	// LONGKEY_NET_HTTP_CLIENT_H_